(setq customary-programming-packages
      '(
        lsp-mode
        cmake-font-lock
        paredit
        lispy
        caps-lock
        racket-mode
        company
        yasnippet
        json-mode
        yasnippet
        (cc-mode :location built-in)
        (python :location built-in)
        (emacs-lisp :location built-in)
        (eldoc :location built-in)
        dumb-jump
        graphviz-dot-mode
        ))


(defun customary-programming/post-init-lsp-mode ()
  (progn
    (setq lsp-ui-doc-enable nil)

    (defun lsp--auto-configure ()
      "Autoconfigure `lsp-ui', `company-lsp' if they are installed."

      (with-no-warnings
        (when (functionp 'lsp-ui-mode)
          (lsp-ui-mode))

        (cond
         ((eq :none lsp-prefer-flymake))
         ((and (not (version< emacs-version "26.1")) lsp-prefer-flymake)
          (lsp--flymake-setup))
         ((and (functionp 'lsp-ui-mode) (featurep 'flycheck))
          (require 'lsp-ui-flycheck)
          (lsp-ui-flycheck-enable t)
          ;; (flycheck-mode -1)
          ))

        (with-eval-after-load 'rust-mode
          (add-hook 'flycheck-mode-hook 'flycheck-rust-setup))

        (when (functionp 'company-lsp)
          (company-mode 1)

          ;; make sure that company-capf is disabled since it is not indented to be
          ;; used in combination with lsp-mode (see #884)
          (setq-local company-backends (remove 'company-capf company-backends))

          (when (functionp 'yas-minor-mode)
            (yas-minor-mode t)))))

    (add-hook 'lsp-after-open-hook 'customary/refresh-imenu-index)

    (defun hidden-lsp-ui-sideline ()
      (interactive)
      (if (< (window-width) 180)
          (progn

            (setq lsp-ui-sideline-show-code-actions nil)
            (setq lsp-ui-sideline-show-diagnostics nil)
            (setq lsp-ui-sideline-show-hover nil)
            (setq lsp-ui-sideline-show-symbol nil))
        (progn

          (setq lsp-ui-sideline-show-code-actions nil)
          ;; (setq lsp-ui-sideline-show-diagnostics t)
          (setq lsp-ui-sideline-show-hover t)
          ;; (setq lsp-ui-sideline-show-symbol t)
          )))

    (advice-add 'lsp-ui-sideline--run :after 'hidden-lsp-ui-sideline)

    (setq lsp-auto-configure t)
    (setq lsp-prefer-flymake nil)))


(defun customary-programming/post-init-ansi-color ()
  (progn
    (defun customary/colorize-compilation ()
      "Colorize from `compilation-filter-start' to `point'."
      (let ((inhibit-read-only t))
        (ansi-color-apply-on-region
         compilation-filter-start (point))))

    (add-hook 'compilation-filter-hook
              'customary/colorize-compilation)
    )
  )


(defun customary-programming/post-init-racket-mode ()
  (progn
    (eval-after-load 'racket-repl-mode
      '(progn
         (define-key racket-repl-mode-map (kbd "]") nil)
         (define-key racket-repl-mode-map (kbd "[") nil)))
    ))


(defun customary-programming/post-init-json-mode ()
  (add-to-list 'auto-mode-alist '("\\.tern-project\\'" . json-mode))
  (add-to-list 'auto-mode-alist '("\\.fire\\'" . json-mode))
  (add-to-list 'auto-mode-alist '("\\.fire.meta\\'" . json-mode))
  (spacemacs/set-leader-keys-for-major-mode 'json-mode
    "ti" 'my-toggle-web-indent))


(defun customary-programming/init-cmake-font-lock ()
  (use-package cmake-font-lock
    :defer t))


(defun customary-programming/init-paredit ()
  (use-package paredit
    :commands (paredit-wrap-round
               paredit-wrap-square
               paredit-wrap-curly
               paredit-splice-sexp-killing-backward)
    :init
    (progn

      (bind-key* "s-(" #'paredit-wrap-round)
      (bind-key* "s-[" #'paredit-wrap-square)
      (bind-key* "s-{" #'paredit-wrap-curly)
      )))


(defun customary-programming/init-lispy ()
  (use-package lispy
    :defer t
    :init
    (progn
      (add-hook 'emacs-lisp-mode-hook (lambda () (lispy-mode 1)))
      (add-hook 'ielm-mode-hook (lambda () (lispy-mode 1)))
      (add-hook 'inferior-emacs-lisp-mode-hook (lambda () (lispy-mode 1)))
      ;; (add-hook 'spacemacs-mode-hook (lambda () (lispy-mode 1)))
      (add-hook 'clojure-mode-hook (lambda () (lispy-mode 1)))
      (add-hook 'scheme-mode-hook (lambda () (lispy-mode 1)))
      (add-hook 'cider-repl-mode-hook (lambda () (lispy-mode 1)))
      )
    :config
    (progn
      (push '(cider-repl-mode . ("[`'~@]+" "#" "#\\?@?")) lispy-parens-preceding-syntax-alist)

      (spacemacs|hide-lighter lispy-mode)
      (define-key lispy-mode-map (kbd "M-s") 'lispy-splice)
      (define-key lispy-mode-map (kbd "DEL") 'lispy-delete-backward)
      (define-key lispy-mode-map (kbd "s-k") 'paredit-splice-sexp-killing-backward)


      (with-eval-after-load 'cider-repl
        (define-key cider-repl-mode-map (kbd "C-s-j") 'cider-repl-newline-and-indent))

      (add-hook
       'minibuffer-setup-hook
       'conditionally-enable-lispy)
      (define-key lispy-mode-map (kbd "s-m") 'lispy-mark-symbol)
      (define-key lispy-mode-map (kbd "s-u") 'lispy-undo)
      (define-key lispy-mode-map (kbd "s-1") 'lispy-describe-inline)
      (define-key lispy-mode-map (kbd "s-2") 'lispy-arglist-inline))))


(defun customary-programming/init-caps-lock ()
  (use-package caps-lock
    :init
    (progn
      (bind-key* "s-e" 'caps-lock-mode))))



(defun customary-programming/post-init-emacs-lisp ()
  :post-config
  (remove-hook 'emacs-lisp-mode-hook 'auto-compile-mode))


(defun customary-programming/post-init-python ()
  :post-config
  (progn
    (add-hook 'python-mode-hook (lambda () (modify-syntax-entry ?_ "w")))
    ;; if you use pyton3, then you could comment the following line
    (setq python-shell-interpreter "python3")
    (setq python-indent-offset 4))
  )


(defun customary-programming/post-init-yasnippet ()
  :post-config
  (progn
    (set-face-background 'secondary-selection "gray")
    (setq-default yas-prompt-functions '(yas-ido-prompt yas-dropdown-prompt))
    (mapc (lambda (hook) (remove-hook hook 'spacemacs/load-yasnippet)) '(prog-mode-hook
                                                                           org-mode-hook
                                                                           markdown-mode-hook))
    (defun customary/load-yasnippet ()
      (interactive)
      (unless yas-global-mode
        (progn
          (yas-global-mode 1)
          (setq my-snippet-dir (expand-file-name "~/.spacemacs.d/snippets"))
          (setq yas-snippet-dirs  my-snippet-dir)
          (yas-load-directory my-snippet-dir)
          (setq yas-wrap-around-region t)))
      (yas-minor-mode 1))

    (spacemacs/add-to-hooks 'customary/load-yasnippet '(prog-mode-hook
                                                        org-mode-hook
                                                        markdown-mode-hook
                                                        ))
    ))


(defun customary-programming/post-init-cc-mode ()
  :post-config
  (progn
    (require 'company)
    (setq company-backends-c-mode-common '((company-dabbrev-code :with company-keywords)
                                           company-files company-dabbrev))

    (defun my-project-name-contains-substring (REGEX)
      (let ((dir (if (buffer-file-name)
                     (file-name-directory (buffer-file-name))
                   "")))
        (string-match-p REGEX dir)))

    (defun my-create-tags-if-needed (SRC-DIR &optional FORCE)
      "return the full path of tags file"
      (let ((dir (file-name-as-directory (file-truename SRC-DIR)))
            file)
        (setq file (concat dir "TAGS"))
        (when (or FORCE (not (file-exists-p file)))
          (message "Creating TAGS in %s ..." dir)
          (shell-command
           (format "ctags -f %s -e -R %s" file dir)))
        file))

    (defvar my-tags-updated-time nil)

    (defun my-update-tags ()
      (interactive)
      "check the tags in tags-table-list and re-create it"
      (dolist (tag tags-table-list)
        (my-create-tags-if-needed (file-name-directory tag) t)))

    (defun my-auto-update-tags-when-save ()
      (interactive)
      (cond
       ((not my-tags-updated-time)
        (setq my-tags-updated-time (current-time)))
       ((< (- (float-time (current-time)) (float-time my-tags-updated-time)) 300)
        ;; < 300 seconds
        ;; do nothing
        )
       (t
        (setq my-tags-updated-time (current-time))
        (my-update-tags)
        (message "updated tags after %d seconds." (- (float-time (current-time)) (float-time my-tags-updated-time))))))

    ;; http://stackoverflow.com/questions/23553881/emacs-indenting-of-c11-lambda-functions-cc-mode
    (defadvice c-lineup-arglist (around my activate)
      "Improve indentation of continued C++11 lambda function opened as argument."
      (setq ad-return-value
            (if (and (equal major-mode 'c++-mode)
                     (ignore-errors
                       (save-excursion
                         (goto-char (c-langelem-pos langelem))
                         ;; Detect "[...](" or "[...]{". preceded by "," or "(",
                         ;;   and with unclosed brace.
                         (looking-at ".*[(,][ \t]*\\[[^]]*\\][ \t]*[({][^}]*$"))))
                0                       ; no additional indent
              ad-do-it)))               ; default behavior

    (setq c-default-style "linux") ;; set style to "linux"
    (setq c-basic-offset 4)
    (c-set-offset 'substatement-open 0))
  (when (configuration-layer/package-usedp 'spell-checking)
    (remove-hook 'c-mode-hook 'flycheck-mode)
    (remove-hook 'c++-mode-hook 'flycheck-mode)
    )

  ;; return nil to write content to file
  (defun customary/untabify-buffer ()
    (interactive)
    (save-excursion
      (untabify (point-min) (point-max)) nil))

  (add-hook 'c++-mode-hook
            '(lambda ()
               (add-hook 'write-contents-hooks
                         'customary/untabify-buffer nil t)))
  ;; company backend should be grouped
  )


(defun customary-programming/post-init-graphviz-dot-mode ()
  (with-eval-after-load 'graphviz-dot-mode
    (require 'company-keywords)
    (push '(graphviz-dot-mode  "digraph" "node" "shape" "subgraph" "label" "edge" "bgcolor" "style" "record") company-keywords-alist)))


(defun customary-programming/post-init-yasnippet ()
  (progn
    (set-face-background 'secondary-selection "gray")

    (with-eval-after-load 'yasnippet
      (progn
        (define-key yas-keymap [(tab)]       (yas-filtered-definition 'yas-next-field))
        (define-key yas-keymap (kbd "TAB")   (yas-filtered-definition 'yas-next-field))))

    (setq-default yas-prompt-functions '(yas-ido-prompt yas-dropdown-prompt))
    (mapc (lambda (hook) (remove-hook hook 'spacemacs/load-yasnippet)) '(prog-mode-hook
                                                                           org-mode-hook
                                                                           markdown-mode-hook))

    (spacemacs/add-to-hooks 'customary/load-yasnippet '(prog-mode-hook
                                                        markdown-mode-hook
                                                        org-mode-hook))
    ))


(defun customary-programming/post-init-company ()
  (progn
    (setq company-dabbrev-code-other-buffers 'all)
    ;; enable dabbrev-expand in company completion https://emacs-china.org/t/topic/6381
    (setq company-dabbrev-char-regexp "[\\.0-9a-z-_'/]")


    (setq company-minimum-prefix-length 1
          company-idle-delay 0.08)

    (when (configuration-layer/package-usedp 'company)
      (spacemacs|add-company-backends :modes shell-script-mode makefile-bsdmake-mode sh-mode lua-mode nxml-mode conf-unix-mode json-mode graphviz-dot-mode js2-mode js-mode))
    ))


(defun customary-programming/post-init-eldoc ()
  (setq eldoc-idle-delay 0.4))


(defun customary-programming/post-init-dumb-jump ()
  (setq dumb-jump-selector 'helm)
  (defun my-dumb-jump ()
    (interactive)
    (evil-set-jump)
    (dumb-jump-go))
  (global-set-key (kbd "C-s-g") 'my-dumb-jump))
